Imports Clo = Enhancements.Core.Logging
Imports Ere = Enhancements.Basic.RegularExpressions
Imports Gen = System.Collections.Generic
Imports Glo = System.Globalization
Imports Iou = System.IO
Imports Pri = Enhancements.Basic.Primitives
Imports Ref = System.Reflection
Imports Reg = System.Text.RegularExpressions
Imports Sys = System
Imports Tex = System.Text
Imports Xsd = System.Xml.Schema
Imports W3c = System.Xml
Imports Vbn = Microsoft.VisualBasic

Namespace Enhancements.Basic.Expressions.Values
    ''' <summary>Represents a token extracted from an expression that has a time value.</summary>
    ''' <remarks>The representations allowed are ruled by the regular expressions defined by the RepresentationsRegularExpressions property with case-insensitivity. The usage of a partial representation leads to zeros-fulfilling in the unspecified parts.</remarks>
    <Serializable(), DebuggerStepThrough()> _
    Public NotInheritable Class DateTime
        Inherits Value

        Private Sub New(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32, ByVal _expressionEndIndex As Sys.Int32, ByVal _value As System.DateTime)
            MyBase.New(_expression, _expressionBeginIndex, _expressionEndIndex, _value)
        End Sub

        ''' <summary>The associated framework type used for the token value storage.</summary>
        Public Shared ReadOnly Property UnderlyingType() As Sys.Type
            Get
                Dim _returnValue As Sys.Type = GetType(System.DateTime)

                Return _returnValue
            End Get
        End Property

        Private Shared __RepresentationsRegularExpressions() As System.String = Nothing
        ''' <summary>The regular expressions used for the parsing and representation of time tokens.</summary>
        Public Shared ReadOnly Property RepresentationsRegularExpressions() As System.String()
            Get
                If __RepresentationsRegularExpressions.IsNull("__RepresentationsRegularExpressions", False) Then
                    Dim _representationsRegularExpressionsList As Gen.List(Of System.String) = New Gen.List(Of System.String)()
                    _representationsRegularExpressionsList.Add("^#(\d{1,4})-(\d{1,2})-(\d{1,2})#.*$")
                    _representationsRegularExpressionsList.Add("^#(\d{1,4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})#.*$")
                    _representationsRegularExpressionsList.Add("^#(\d{1,4})-(\d{1,2})-(\d{1,2}) (\d{1,2}):(\d{1,2}):(\d{1,2})(\.(\d{1,3}))#.*$")
                    __RepresentationsRegularExpressions = _representationsRegularExpressionsList.ToArray()
                End If

                Return __RepresentationsRegularExpressions
            End Get
        End Property

        ''' <summary>The minimum value accepted for a time token.</summary>
        Public Shared ReadOnly Property Minimum() As System.DateTime
            Get
                Dim _returnValue As System.DateTime = New Sys.DateTime(1, 1, 1, 0, 0, 0, 0)

                Return _returnValue
            End Get
        End Property

        ''' <summary>The maximum value accepted for a time token.</summary>
        Public Shared ReadOnly Property Maximum() As System.DateTime
            Get
                Dim _returnValue As System.DateTime = Sys.DateTime.MaxValue

                Return _returnValue
            End Get
        End Property

        Private Shared Function __Parse(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As Sys.Object
            Dim _returnValue As Sys.Object = Nothing
            If _expression.IsNullNot("_expression", False) AndAlso _expression.IsEmptyNot("_expression", False) AndAlso _expressionBeginIndex.IsInRange("_expressionBeginIndex", 0, _expression.Length - 1, False) Then
                If ___Texts.StartsWith(_expression.Substring(_expressionBeginIndex), "#") Then
                    If ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length) <> -1 Then
                        _returnValue = __ParseWithRegularExpression2(_expression, _expressionBeginIndex)
                        If Assert.IsNull(_returnValue, "_returnValue", False) Then
                            _returnValue = __ParseWithRegularExpression1(_expression, _expressionBeginIndex)
                            If Assert.IsNull(_returnValue, "_returnValue", False) Then
                                _returnValue = __ParseWithRegularExpression0(_expression, _expressionBeginIndex)
                            End If
                        End If
                    End If
                End If
            End If
            If Assert.IsNull(_returnValue, "_returnValue", False) Then
                _returnValue = DateTimeParseException.___Create(_expression, _expressionBeginIndex)
            End If

            Return _returnValue
        End Function

        Private Shared Function __ParseWithRegularExpression2(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As DateTime
            Dim _returnValue As DateTime = Nothing
            Dim _regularExpression As Reg.Regex = New Reg.Regex(RepresentationsRegularExpressions(2))
            Dim _expressionPart As System.String = _expression.Substring(_expressionBeginIndex, ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length) - _expressionBeginIndex + 1)
            Dim _expressionPartYear As Sys.Int32 = 0
            Dim _expressionPartMonth As Sys.Int32 = 0
            Dim _expressionPartDay As Sys.Int32 = 0
            Dim _expressionPartHour As Sys.Int32 = 0
            Dim _expressionPartMinute As Sys.Int32 = 0
            Dim _expressionPartSecond As Sys.Int32 = 0
            Dim _expressionPartMillisecond As Sys.Int32 = 0
            If _regularExpression.IsMatch(_expressionPart) Then
                Dim _regularExpressionGroups() As System.String = _regularExpression.Split(_expressionPart)
                _expressionPartYear = Sys.Int32.Parse(_regularExpressionGroups(1), Glo.CultureInfo.InvariantCulture)
                _expressionPartMonth = Sys.Int32.Parse(_regularExpressionGroups(2), Glo.CultureInfo.InvariantCulture)
                _expressionPartDay = Sys.Int32.Parse(_regularExpressionGroups(3), Glo.CultureInfo.InvariantCulture)
                _expressionPartHour = Sys.Int32.Parse(_regularExpressionGroups(4), Glo.CultureInfo.InvariantCulture)
                _expressionPartMinute = Sys.Int32.Parse(_regularExpressionGroups(5), Glo.CultureInfo.InvariantCulture)
                _expressionPartSecond = Sys.Int32.Parse(_regularExpressionGroups(6), Glo.CultureInfo.InvariantCulture)
                _expressionPartMillisecond = Sys.Int32.Parse(_regularExpressionGroups(8), Glo.CultureInfo.InvariantCulture)
                Dim _dummy As Sys.DateTime = Sys.DateTime.Now
                Try
                    _dummy = New Sys.DateTime(_expressionPartYear, _expressionPartMonth, _expressionPartDay, _expressionPartHour, _expressionPartMinute, _expressionPartSecond, _expressionPartMillisecond)
                    If _dummy.IsInRange("_dummy", Minimum, Maximum, False) Then
                        _returnValue = New DateTime(_expression, _expressionBeginIndex, ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length), _dummy)
                    End If
                Catch
                End Try
            End If

            Return _returnValue
        End Function

        Private Shared Function __ParseWithRegularExpression1(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As DateTime
            Dim _returnValue As DateTime = Nothing
            Dim _regularExpression As Reg.Regex = New Reg.Regex(RepresentationsRegularExpressions(1))
            Dim _expressionPart As System.String = _expression.Substring(_expressionBeginIndex, ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length) - _expressionBeginIndex + 1)
            Dim _expressionPartYear As Sys.Int32 = 0
            Dim _expressionPartMonth As Sys.Int32 = 0
            Dim _expressionPartDay As Sys.Int32 = 0
            Dim _expressionPartHour As Sys.Int32 = 0
            Dim _expressionPartMinute As Sys.Int32 = 0
            Dim _expressionPartSecond As Sys.Int32 = 0
            If _regularExpression.IsMatch(_expressionPart) Then
                Dim _regularExpressionGroups() As System.String = _regularExpression.Split(_expressionPart)
                _expressionPartYear = Sys.Int32.Parse(_regularExpressionGroups(1), Glo.CultureInfo.InvariantCulture)
                _expressionPartMonth = Sys.Int32.Parse(_regularExpressionGroups(2), Glo.CultureInfo.InvariantCulture)
                _expressionPartDay = Sys.Int32.Parse(_regularExpressionGroups(3), Glo.CultureInfo.InvariantCulture)
                _expressionPartHour = Sys.Int32.Parse(_regularExpressionGroups(4), Glo.CultureInfo.InvariantCulture)
                _expressionPartMinute = Sys.Int32.Parse(_regularExpressionGroups(5), Glo.CultureInfo.InvariantCulture)
                _expressionPartSecond = Sys.Int32.Parse(_regularExpressionGroups(6), Glo.CultureInfo.InvariantCulture)
                Dim _dummy As Sys.DateTime = Sys.DateTime.Now
                Try
                    _dummy = New Sys.DateTime(_expressionPartYear, _expressionPartMonth, _expressionPartDay, _expressionPartHour, _expressionPartMinute, _expressionPartSecond, 0)
                    If _dummy.IsInRange("_dummy", Minimum, Maximum, False) Then
                        _returnValue = New DateTime(_expression, _expressionBeginIndex, ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length), _dummy)
                    End If
                Catch
                End Try
            End If

            Return _returnValue
        End Function

        Private Shared Function __ParseWithRegularExpression0(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As DateTime
            Dim _returnValue As DateTime = Nothing
            Dim _regularExpression As Reg.Regex = New Reg.Regex(RepresentationsRegularExpressions(0))
            Dim _expressionPart As System.String = _expression.Substring(_expressionBeginIndex, ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length) - _expressionBeginIndex + 1)
            Dim _expressionPartYear As Sys.Int32 = 0
            Dim _expressionPartMonth As Sys.Int32 = 0
            Dim _expressionPartDay As Sys.Int32 = 0
            If _regularExpression.IsMatch(_expressionPart) Then
                Dim _regularExpressionGroups() As System.String = _regularExpression.Split(_expressionPart)
                _expressionPartYear = Sys.Int32.Parse(_regularExpressionGroups(1), Glo.CultureInfo.InvariantCulture)
                _expressionPartMonth = Sys.Int32.Parse(_regularExpressionGroups(2), Glo.CultureInfo.InvariantCulture)
                _expressionPartDay = Sys.Int32.Parse(_regularExpressionGroups(3), Glo.CultureInfo.InvariantCulture)
                Dim _dummy As Sys.DateTime = Sys.DateTime.Now
                Try
                    _dummy = New Sys.DateTime(_expressionPartYear, _expressionPartMonth, _expressionPartDay, 0, 0, 0, 0)
                    If _dummy.IsInRange("_dummy", Minimum, Maximum, False) Then
                        _returnValue = New DateTime(_expression, _expressionBeginIndex, ___Texts.IndexOf(_expression, "#", _expressionBeginIndex + "#".Length), _dummy)
                    End If
                Catch
                End Try
            End If

            Return _returnValue
        End Function

        ''' <summary>Parses an expression for a time value starting by a begin index.</summary>
        ''' <param name="_expression">Expression to parse.</param>
        ''' <param name="_expressionBeginIndex">Expression begin index to use.</param>
        ''' <exception cref="DateTimeParseException">The operation did not produce a valid token.</exception>
        ''' <returns> </returns>
        Public Overloads Shared Function Parse(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32) As DateTime
            Dim _returnValue As Sys.Object = __Parse(_expression, _expressionBeginIndex)
            If Assert.IsOfType(_returnValue, "_returnValue", GetType(DateTimeParseException), False) Then
                DateTimeParseException.___Throw(CType(_returnValue, DateTimeParseException))
            End If

            Return CType(_returnValue, DateTime)
        End Function

        ''' <summary>Tries to parse an expression for a time value starting by a begin index.</summary>
        ''' <param name="_expression">Expression to parse.</param>
        ''' <param name="_expressionBeginIndex">Expression begin index to use.</param>
        ''' <param name="_result">The result of the operation if possible. If not, null is returned.</param>
        ''' <returns> </returns>
        Public Overloads Shared Function TryParse(ByVal _expression As System.String, ByVal _expressionBeginIndex As Sys.Int32, ByRef _result As DateTime) As System.Boolean
            Dim _returnValue As System.Boolean = False
            Dim _dummy As Sys.Object = __Parse(_expression, _expressionBeginIndex)
            If Assert.IsOfType(_dummy, "_dummy", GetType(DateTimeParseException), False) Then
                _result = Nothing
                _returnValue = False
            Else
                _result = CType(_dummy, [DateTime])
                _returnValue = True
            End If

            Return _returnValue
        End Function

        ''' <summary>Determines the token's correct representation for use in an expression.</summary>
        ''' <returns> </returns>
        Public Overrides Function ToRepresentation() As System.String
            Dim _returnValue As System.String = __ToRepresentation(CType(Value, System.DateTime))

            Return _returnValue
        End Function

        ''' <summary>Determines the correct representation of a time value for use in an expression.</summary>
        ''' <exception cref="DateTimeRepresentationException">The operation did not produce a valid representation because of the input value nature.</exception>
        ''' <returns> </returns>
        Public Overloads Shared Function ToRepresentation(ByVal _value As System.DateTime) As System.String
            If Not ___AssertIsValidValue(_value) Then
                DateTimeRepresentationException.___Throw(_value)
            End If

            Dim _returnValue As System.String = __ToRepresentation(_value)

            Return _returnValue
        End Function

        Private Shared Function __ToRepresentation(ByVal _value As System.DateTime) As System.String
            Dim _returnValueStringBuilder As Tex.StringBuilder = New Tex.StringBuilder(Sys.String.Empty)
            Dim _valueDateTime As System.DateTime = CType(_value, System.DateTime)
            _returnValueStringBuilder.Append("#")
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Year.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(4, "0"c)))
            _returnValueStringBuilder.Append(___Texts.ToUpper("-"))
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Month.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(2, "0"c)))
            _returnValueStringBuilder.Append(___Texts.ToUpper("-"))
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Day.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(2, "0"c)))
            _returnValueStringBuilder.Append(___Texts.ToUpper(" "))
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Hour.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(2, "0"c)))
            _returnValueStringBuilder.Append(___Texts.ToUpper(":"))
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Minute.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(2, "0"c)))
            _returnValueStringBuilder.Append(___Texts.ToUpper(":"))
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Second.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(2, "0"c)))
            _returnValueStringBuilder.Append(___Texts.ToUpper("."))
            _returnValueStringBuilder.Append(___Texts.ToUpper(_valueDateTime.Millisecond.ToString(Glo.CultureInfo.InvariantCulture).PadLeft(3, "0"c)))
            _returnValueStringBuilder.Append("#")

            Return _returnValueStringBuilder.ToString()
        End Function

        Friend Overloads Shared Function ___AssertIsValidValue(ByVal _value As Sys.Object) As System.Boolean
            Dim _returnValue As System.Boolean = False
            If Assert.IsNullNot(_value, "_value", False) Then
                If Assert.IsOfType(_value, "_value", DateTime.UnderlyingType, False) Then
                    Dim _valueDateTime As Sys.DateTime = CType(_value, Sys.DateTime)
                    If _valueDateTime.IsInRange("_valueDateTime", Minimum, Maximum, False) Then
                        _returnValue = True
                    End If
                End If
            End If

            Return _returnValue
        End Function

        ''' <exception cref="IsNullException"> </exception>
        ''' <exception cref="IsListFilledNotException"> </exception>
        ''' <exception cref="CompilerExpressionDateTimesDelimitersCountIsValidNotException"> </exception>
        ''' <exception cref="CompilerExpressionDateTimesAreValidNotException"> </exception>
        Friend Shared Function ___ParseAll(ByRef _tokens As Gen.List(Of Token)) As Sys.Int32
            _tokens.IsListFilled("_tokens", True)
            For _counter1 As Sys.Int32 = 0 To _tokens.Count - 1
                If _tokens(_counter1).IsOfType("_tokens(_counter1)", GetType(Undetermined), False) Then
                    Dim _tokensToken As Undetermined = CType(_tokens(_counter1), Undetermined)
                    Dim _delimiterRepresentationOccurrences() As Pri.Occurrence = Pri.String.FindOccurrences(___Texts.ToUpper(_tokensToken.ExpressionPart), ___Texts.ToUpper("#"))
                    If Assert.IsInRangeNot(_delimiterRepresentationOccurrences.Length Mod 2, "_delimiterRepresentationOccurrences.Length Mod 2", 0, 0, False) Then
                        CompilerExpressionDateTimesDelimitersCountIsValidNotException.___Throw(_tokensToken.Expression, _tokensToken.ExpressionPart, _delimiterRepresentationOccurrences.Length)
                    Else
                        If Assert.IsInRange(_delimiterRepresentationOccurrences.Length, "_delimiterRepresentationOccurrences.Length", 2, Sys.Int32.MaxValue, False) Then
                            For _counter2 As Sys.Int32 = 0 To _delimiterRepresentationOccurrences.Length - 1 Step 2
                                Dim _possibleDateTime As System.String = _tokensToken.ExpressionPart.Substring(_delimiterRepresentationOccurrences(_counter2).BeginIndex, _delimiterRepresentationOccurrences(_counter2 + 1).EndIndex - _delimiterRepresentationOccurrences(_counter2).BeginIndex + 1)
                                Dim _possibleDateTimeToken As DateTime = Nothing
                                If TryParse(_possibleDateTime, 0, _possibleDateTimeToken).IsTrueNot("TryParse(_possibleDateTime, 0, _possibleDateTimeToken)", False) Then
                                    CompilerExpressionDateTimesAreValidNotException.___Throw(_tokensToken.Expression, _possibleDateTime)
                                Else
                                    If _possibleDateTimeToken.ExpressionPart.Length.IsInRangeNot("_possibleDateTimeToken.ExpressionPart.Length", _delimiterRepresentationOccurrences(_counter2 + 1).EndIndex - _delimiterRepresentationOccurrences(_counter2).BeginIndex + 1, _delimiterRepresentationOccurrences(_counter2 + 1).EndIndex - _delimiterRepresentationOccurrences(_counter2).BeginIndex + 1, False) Then
                                        CompilerExpressionDateTimesAreValidNotException.___Throw(_tokensToken.Expression, _possibleDateTime)
                                    End If
                                End If
                            Next _counter2
                        End If
                    End If
                End If
            Next _counter1

            Dim _returnValue As Sys.Int32 = 0
            Dim _tokensNew As Gen.List(Of Token) = New Gen.List(Of Token)()
            For _counter1 As Sys.Int32 = 0 To _tokens.Count - 1
                If _tokens(_counter1).IsOfType("_tokens(_counter1)", GetType(Undetermined), False) Then
                    Dim _tokensToken As Undetermined = CType(_tokens(_counter1), Undetermined)
                    For _counter2 As Sys.Int32 = 0 To _tokensToken.ExpressionPart.Length - 1
                        Dim _dateTime As DateTime = Nothing
                        If TryParse(_tokensToken.Expression, _tokensToken.ExpressionBeginIndex + _counter2, _dateTime) Then
                            _tokensNew.Add(_dateTime)
                            _counter2 += _dateTime.ExpressionPart.Length - 1
                            _returnValue += 1
                        Else
                            Undetermined.___AppendNewToEndOrReplaceLastWithNew(_tokensNew, _tokensToken.Expression, _tokensToken.ExpressionBeginIndex + _counter2, _tokensToken.ExpressionBeginIndex + _counter2)
                        End If
                    Next _counter2
                Else
                    _tokensNew.Add(_tokens(_counter1))
                End If
            Next _counter1
            _tokens = _tokensNew

            Return _returnValue
        End Function

    End Class
End Namespace
